package com.example.mykotlin.base.auth

import com.example.mykotlin.base.result.ServiceException
import java.security.Key
import java.security.KeyFactory
import java.security.spec.PKCS8EncodedKeySpec
import javax.crypto.Cipher
import org.apache.commons.codec.binary.Base64

class PoiuyCipher {
    companion object {
        private const val EXPIRED_TIME = -1
        private const val PRIVATE_KEY =
            "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJRABe4zHoibdSm76/kZhP+VxQbFhOtt3JDYm6vammxxvkH1UU261OjlgcU2Z7W1wQg+w804hRkoCyazBr79jP5PX+90vWmRYqCw6AyTaE66a0CdbAZlUIEhvsBsApGlr2+LL2oQAvgyJko0QNRw+ggCHcWNFw27J88Ab2MlbpkVAgMBAAECgYADlOW6cthiqy7SH/d8Kyuqvm7mJ6Ylx0pe4tIlUsC3AgB7Y3aKuYrPSEwxpuDWjlo9iBapl0X6l5xoBkc8HLASozzUQiTg6W+BZIiWBMvNwutYw7ezSX7YRZ4SBVQjp+51f3dhz0GtglUXmY4aePxaPIWBr6d0K2AsEXAcy7f4+QJBAKJIUTsh64JiC+rqIFsfOV9DE4owCjvljZQ4fuLi3IFZwkLF/uFm3K9RW8mjrpI/GGopKpCapAYGiZkHzqqjoHsCQQDp3St8yr/CZ350ZhDcQxCarmzSfEfaK9MIkI3yn7XG+Gxn2hFl4YIEZ8Bku8FzIXYwaU7OmVQ7KjMki8Js3x+vAkBqe99pWLGLRUCirWv7vrpLqtzYcPtWICNNXeDMiaW87qPMYtUDHBa0p0L+RXwYFHS1vDtk1UPJfGKvWnMefu2fAkEAuAqk6Y1JQX1e581W4XApxgEUhMTnastoRWo1O0XysMXZ0hSr8mh4gG0B3IRhpfAkac7GBRJnVjRMkSsnc7RX4wJACHYe9Ua61Eo2pW/Unb+wd9eggNN7eVtzdkXuE0WcFVqNQlMb4wk2JStJo8X8DVDw3XcGfnxhQY/TOf/C/6P9xg=="
        const val PUBLIC_KEY =
            "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCUQAXuMx6Im3Upu+v5GYT/lcUGxYTrbdyQ2Jur2ppscb5B9VFNutTo5YHFNme1tcEIPsPNOIUZKAsmswa+/Yz+T1/vdL1pkWKgsOgMk2hOumtAnWwGZVCBIb7AbAKRpa9viy9qEAL4MiZKNEDUcPoIAh3FjRcNuyfPAG9jJW6ZFQIDAQAB"

        private const val DEVICE_EXPIRED_TIME = -1
        private const val DEVICE_PRIVATE_KEY =
            "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMYpk/UXjnxVLj7BOHF8u85TThGusPaMu/R7MUqwuldB6GIGDMZvL89E7vME31+6dOSrc/hZXxGTbUUqG0x3Lz5wZJsc+GRjFqX82GE6DpEFHiizs4DYxdsx1iUy7TjlPQ73JpsjLEcvvSS1rFiUvgoN7L2vwgKd1o/Pr+nUsRJPAgMBAAECgYAC0c+hoqKr4ThluzEW6EvxzRykwsQQ1gLMoT4bznTwTedFEcn3vthCzjvKKG0XOxtzr37Yb0kbgmoBHG7EAUQiTGQl79VBBqNO/69sXw/sHUXym8gCpd5xf6kNeFFpUHGAhGFeWvh2WOVRKObUIoN+kMgV5WEfFbADq5vg6SgjPQJBANkhPMwYmdIH+CZ2bfT4LQnei5HC3x9g2S5vKQd6drrAT23bC1YrrAq5as3cNE4co0KnCKl3HXpsmRig9K9T4asCQQDpoxbxbB2BLhNHPHYWeU95pyT2ZOo7xYqzf+56i7RXj5B8j5SmNNEg/R9I3OW55eAPlshAWd3qa+ypLmLPsnXtAkEAxqfzUSSc754c+0vVlJDP3DzB442wyKu6aRKJLm9sW7cBtRf5ClEQqgs03b5AGMt4z821Th9OWCkt9WumExei2wJAaxPH7BW04AOR6lAXfu1SM1sbYaGWiJZcny++yINGv+qYOb5ETnhecFQSBn7W+oAUVJCybmvBfTLBYDhWKQKNcQJAaAjGs1tMcCfZzpIva3jJ0UuukG/gWhTwriqmTkZ7n4VfRRaxzdPM7bkr3zNAiufdEu1tc0VhfjCAlLP+qE2E7w=="
        const val DEVICE_PUBLIC_KEY =
            "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDGKZP1F458VS4+wThxfLvOU04RrrD2jLv0ezFKsLpXQehiBgzGby/PRO7zBN9funTkq3P4WV8Rk21FKhtMdy8+cGSbHPhkYxal/NhhOg6RBR4os7OA2MXbMdYlMu045T0O9yabIyxHL70ktaxYlL4KDey9r8ICndaPz6/p1LESTwIDAQAB"

        fun rsaDecrypt(s: String?) = rsaDecryptBase(s, PRIVATE_KEY, EXPIRED_TIME)
        fun rsaDecryptDevice(s: String?) =
            rsaDecryptBase(s, DEVICE_PRIVATE_KEY, DEVICE_EXPIRED_TIME)

        /** 用于RSA解密
         * @param s 待解密的字符串
         * @param privateKey 私钥
         * @param expiredTime 过期时间（单位：毫秒）
         * @return 解密后的字符串
         */
        private fun rsaDecryptBase(
            s: String?,
            privateKey: String = PRIVATE_KEY,
            expiredTime: Int = -1
        ): String {
            // 尝试进行私钥解密
            try {
                // 使用私钥解密
                val rsaDecryptByPrivate = decryptByPrivateKey(s, privateKey)
                // 校验加密信息中的时间，需要前端按约定好的同样的规则加入时间戳信息加密构造
                // 规则：在需要加密的信息前面拼接上（固定13位的）时间戳后再进行公钥加密，校验时解密后取出验证
                // 提取时间戳和密码
                val timestamp = StringBuilder()
                val password = StringBuilder()
                for (i in rsaDecryptByPrivate.indices) {
                    if (i < 13)
                    // 将时间戳提取出来
                        timestamp.append(rsaDecryptByPrivate[i])
                    else
                    // 将密码提取出来
                        password.append(rsaDecryptByPrivate[i])
                }
                // 计算时间戳与当前时间的差值
                val splitTime = System.currentTimeMillis() - timestamp.toString().toLong()
                // 如果过期时间为-1或时间戳有效
                if (expiredTime < 0 || splitTime in 1..<expiredTime)
                // 返回解密后的密码
                    return password.toString()
                else
                // 如果时间戳过期，抛出异常
                    throw ServiceException("值不可用，请重新加密","1")
            } catch (se: ServiceException) {
                // 如果发生ServiceException，直接抛出
                throw se
            } catch (e: Exception) {
                // 如果发生其他异常，抛出ServiceException
                throw ServiceException("未正确加密，无法解析","1")
            }

        }

        private const val KEY_ALGORITHM = "RSA"
        private fun decryptBASE64(key: String?) = Base64.decodeBase64(key)

        /**
         * 解密<br></br>
         * 用私钥解密
         *
         * @param data
         * @param key
         * @return
         * @throws Exception
         */
         private fun decryptByPrivateKey(data: String?, key: String?): String {
            // 对密钥解密
            val keyBytes: ByteArray = decryptBASE64(key)
            // 取得私钥
            val pkcs8KeySpec = PKCS8EncodedKeySpec(keyBytes)
            val keyFactory = KeyFactory.getInstance(KEY_ALGORITHM)
            val privateKey: Key = keyFactory.generatePrivate(pkcs8KeySpec)
            // 对数据解密
            val cipher = Cipher.getInstance(keyFactory.algorithm)
            cipher.init(Cipher.DECRYPT_MODE, privateKey)
            return String(cipher.doFinal(decryptBASE64(data)))
        }

    }
}